﻿using System;
using System.Collections.Generic;
using System.Text;

namespace Weazel.Willows.Model
{
	public partial class Queue
		: Channel, IQueueConnectorReader, IQueueConnectorWriter
	{
		private Weazel.Willows.RefinedModel.QueueIpBlock queueIpBlock;

		#region Signal names
		public string CanReadSignalName
		{
			get { return QueuePrefix + LocalName + "_can_read"; }
		}

		public string DequeueSignalName
		{
			get { return QueuePrefix + LocalName + "_enqueue"; }
		}

		public string DoReadSignalName
		{
			get { return QueuePrefix + LocalName + "_do_read"; }
		}

		public string CanWriteSignalName
		{
			get { return QueuePrefix + LocalName + "_can_write"; }
		}

		public string EnqueueSignalName
		{
			get { return QueuePrefix + LocalName + "_dequeue"; }
		}

		public string DoWriteSignalName
		{
			get { return QueuePrefix + LocalName + "_do_write"; }
		}
		#endregion

		#region Signal insertion
		// Signals for the reading end
		private void insertDataInSignal(Weazel.Gezel.Model.Datapath datapath)
		{
			Weazel.Gezel.Model.Signal signal =
				new Weazel.Gezel.Model.Signal(datapath, DequeueSignalName, Type.BitWidth, false);

			datapath.Signals.Add(signal.LocalName, signal);
		}

		private void insertCanReadSignal(Weazel.Gezel.Model.Datapath datapath)
		{
			Weazel.Gezel.Model.Signal signal =
				new Weazel.Gezel.Model.Signal(datapath, CanReadSignalName, 1, false); // boolean

			datapath.Signals.Add(signal.LocalName, signal);
		}

		private void insertDoReadSignal(Weazel.Gezel.Model.Datapath datapath)
		{
			Weazel.Gezel.Model.Signal signal =
				new Weazel.Gezel.Model.Signal(datapath, DoReadSignalName, 1, false); // boolean

			datapath.Signals.Add(signal.LocalName, signal);
		}

		// signals for the writing end
		private void insertDataOutSignal(Weazel.Gezel.Model.Datapath datapath)
		{
			Weazel.Gezel.Model.Signal dataOut =
				new Weazel.Gezel.Model.Signal(datapath, EnqueueSignalName, Type.BitWidth, false);

			datapath.Signals.Add(dataOut.LocalName, dataOut);
		}

		private void insertCanWriteSignal(Weazel.Gezel.Model.Datapath datapath)
		{
			Weazel.Gezel.Model.Signal canWrite =
				new Weazel.Gezel.Model.Signal(datapath, CanWriteSignalName, 1, false); // boolean

			datapath.Signals.Add(canWrite.LocalName, canWrite);
		}

		private void insertDoWriteSignal(Weazel.Gezel.Model.Datapath datapath)
		{
			Weazel.Gezel.Model.Signal doWrite =
				new Weazel.Gezel.Model.Signal(datapath, DoWriteSignalName, 1, false); // boolean

			datapath.Signals.Add(doWrite.LocalName, doWrite);
		}
		#endregion

		private bool refined = false;

		public override void Refine(RefinedModel.Model model)
		{
			if (refined)
				return;

			refined = true;

			// Parent must not be null 
			if (Parent == null)
			{
				string message =
					string.Format("Parent not set for Queue '{0}'.", Name);

				throw new NullReferenceException(message);
			}

			// Parent must be of type Module
			if (!(Parent is Module))
			{
				string message =
					"Refine(): Parent of Queue must be 'system' or 'Datapath'.";

				throw new NotSupportedException(message);
			}

			// now, parent must be a module
			Module module = Parent as Module;

			// refine the module (if it has not already been)
			module.Refine(model);

			// get the refined datapath
			Weazel.Gezel.Model.Datapath datapath = module.Datapath;

			// Create a queue IP block and insert it into the
			// models list of IP blocks.
			// Todo: LocalName, ok?

			this.queueIpBlock = 
				new Weazel.Willows.RefinedModel.QueueIpBlock(model, LocalName, this);

			// Insert the IP block queue into the parent datapath's
			// list of children.

			datapath.ChildDatapathRefs.Add(
				queueIpBlock.LocalName,
				new string[] // positional binding list for block (order is important)
				{ 						
          EnqueueSignalName,
   				CanWriteSignalName,
					DoWriteSignalName,
					DequeueSignalName,
					CanReadSignalName,
					DoReadSignalName
				}
			);

			insertDataOutSignal(datapath);
			insertCanReadSignal(datapath);
			insertDoReadSignal(datapath);
			insertDataInSignal(datapath);
			insertCanWriteSignal(datapath);
			insertDoWriteSignal(datapath);

			// Refine the writer
			writer.Refine(model);

			// Refine readers
			foreach (IQueueReaderInterface reader in readers)
				reader.Refine(model);
		}
	}
}
